package cn.changeforyou.demo.netty.server;

import cn.changeforyou.demo.netty.common.Data;
import cn.changeforyou.demo.netty.common.SSLContextFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLEngine;

/**
 * @author zhyu
 * @date 2021/3/12 14:23
 */
public class Server {

    public static void main(String[] args) {
        Server server = new Server(443);
        server.setEnableSsl(true);
        server.start();
    }

    private int port;
    private ServerHandler serverHandler;
    private boolean enableSsl;

    public void setEnableSsl(boolean enableSsl) {
        this.enableSsl = enableSsl;
    }

    public Server(int port) {
        this.port = port;
        serverHandler = new ServerHandler();
    }

    public void start() {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(port)).handler(new LoggingHandler())
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
//						YbkjTcpDataDecoder ybkjTcpDataDecoder = new YbkjTcpDataDecoder();
//						YbkjTcpDataEncoder ybkjTcpDataEncoder = new YbkjTcpDataEncoder();
//						ExceptionHandler exceptionHandler = new ExceptionHandler();
//						HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(8192);
//						WebSocketServerProtocolHandler webSocketServerProtocolHandler = new WebSocketServerProtocolHandler(ProtocolSelectorHandler.WEBSOCKET_PREFIX);
//						SSLEngine sslEngine = new SSLContextFactory().getServerSslContext().createSSLEngine();
//						SslHandler sslHandler = new SslHandler(sslEngine);
//						HttpServerCodec httpServerCodec = new HttpServerCodec();
//						ProtocolSelectorHandler protocolSelectorHandler = new ProtocolSelectorHandler(serverHandler, ybkjTcpDataDecoder, ybkjTcpDataEncoder, exceptionHandler, httpObjectAggregator, webSocketServerProtocolHandler, sslHandler, httpServerCodec);
                        if (enableSsl) {
                            SSLEngine sslEngine = new SSLContextFactory().getServerSslContext().createSSLEngine();
                            sslEngine.setUseClientMode(false);
                            SslHandler sslHandler = new SslHandler(sslEngine);
                            ch.pipeline().addLast(sslHandler);
                        }
                        ch.pipeline().addLast(new ProtocolSelectorHandler(serverHandler));
                    }
                });
            sendBroad(workerGroup);
            ChannelFuture future = b.bind().sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private void sendBroad(EventLoopGroup workerGroup) {
        workerGroup.scheduleAtFixedRate(() -> printStatistics(), 10L, 10L, TimeUnit.SECONDS);
        Map<String, Object> pushData = new HashMap<>(2);
        pushData.put("type", "这是一个广播推送");
        final Data broadCastData = new Data(Data.MsgType.json, pushData);
        workerGroup.scheduleAtFixedRate(() -> broadcast(broadCastData), 10L, 10L, TimeUnit.SECONDS);
    }

    public void broadcast(Data data) {
        serverHandler.broadcast(data);
    }

    public void printStatistics() {
        serverHandler.printStatistics();
    }

    public void send(String user, Data data) {
        serverHandler.send(user, data);
    }

}
